﻿using System;
using System.Drawing;
using System.Windows.Forms;

using Microsoft.WowAddonStudio.FrameXml.Serialization;
using Microsoft.WowAddonStudio.FrameXml.Components.Layout;
using Microsoft.WowAddonStudio.FrameXml.Components.Support;

namespace Microsoft.WowAddonStudio.FrameXml.Components
{
    public partial class FrameXmlControl<TSerializationObject>
    {
        /// <summary>
        /// Gets the <see cref="FrameXmlLayoutEngine"/> service.
        /// </summary>
        public new FrameXmlLayoutEngine LayoutEngine
        {
            get
            {
                var layoutEngine = this.GetService<FrameXmlLayoutEngine>();
                if (layoutEngine == null)
                    throw new ApplicationException("Required service 'FrameXmlLayoutEngine' is missing.");

                return layoutEngine;
            }
        }

        /// <summary>
        /// Called after the control has been added to another container.
        /// </summary>
        protected override void InitLayout()
        {
            base.InitLayout();

            // Initialize the layout for this control
            LayoutEngine.InitLayout(this);
        }

        /// <summary>
        /// Forces the control to apply layout logic.
        /// </summary>
        public new void PerformLayout()
        {
            LayoutEngine.Layout(this, null);
        }

        /// <summary>
        /// Forces the control to apply layout logic.
        /// </summary>
        public void PerformLayout(string affectedProperty)
        {
            if (affectedProperty == null)
                throw new ArgumentNullException("affectedProperty");

            LayoutEngine.Layout(this, affectedProperty);
        }

        /// <summary>
        /// Performs the layout.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="affectedProperty">The affected property.</param>
        public new void PerformLayout(Control control, string affectedProperty)
        {
            if (!(control is IFrameXmlControl))
                throw new ArgumentException("Layout can only be performed on FrameXmlControls.");

            LayoutEngine.Layout((IFrameXmlControl)control, affectedProperty);
        }

        /// <summary>
        /// Gets an anchor point of the layout frame.
        /// </summary>
        /// <param name="anchor">The anchor.</param>
        /// <param name="ignoreOffset">True, if the offset should be ignored.</param>
        /// <returns>A <see cref="Point"/> structure holding the coordinates of the anchor point.</returns>
        /// <remarks>
        /// The <see cref="Anchor.RelativePoint"/> <see cref="FramePoint"/> is used if it is specified. The returned point
        /// is in screen coordinates.
        /// </remarks>
        public Point GetAnchorPoint(Anchor anchor, bool ignoreOffset)
        {
            if (anchor == null)
                throw new ArgumentNullException("anchor");

            // Determine the frame point needed to return for this anchor
            var framePoint = anchor.RelativePoint.HasValue ? anchor.RelativePoint.Value : anchor.Point;

            // Get the anchor point
            var point = this.GetFramePoint(framePoint);

            // Apply the anchor offset (Dimension.Offset returns a new value as it is an extension method.. How stupid.)
            if (!ignoreOffset && anchor.Offset != null)
                point = point.Offset(anchor.Offset);

            // Return the resulting point
            return point;
        }

        /// <summary>
        /// Gets an anchor point of the layout frame.
        /// </summary>
        /// <param name="anchor">The anchor.</param>
        /// <returns>A <see cref="Point"/> structure holding the coordinates of the anchor point.</returns>
        /// <remarks>
        /// The <see cref="Anchor.RelativePoint"/> <see cref="FramePoint"/> is used if it is specified. The returned point
        /// is in screen coordinates.
        /// </remarks>
        public Point GetAnchorPoint(Anchor anchor)
        {
            return this.GetAnchorPoint(anchor, false);
        }

        /// <summary>
        /// Gets a frame point of the layout frame.
        /// </summary>
        /// <param name="framePoint">The frame point.</param>
        /// <returns>A <see cref="Point"/>structure holding the coordinates of the frame point.</returns>
        /// <remarks>The point returned is in the control's client coordinate system.</remarks>
        public Point GetFramePoint(FramePoint framePoint)
        {
            int x, y;

            // Determine x coordinate
            switch (framePoint)
            {
                case FramePoint.TopLeft:
                case FramePoint.Left:
                case FramePoint.BottomLeft:
                    x = 0;
                    break;
                case FramePoint.Top:
                case FramePoint.Center:
                case FramePoint.Bottom:
                    x = this.Width / 2;
                    break;
                case FramePoint.TopRight:
                case FramePoint.Right:
                case FramePoint.BottomRight:
                    x = this.Width;
                    break;
                default:
                    throw new ArgumentException("Unknown FramePoint.", "framePoint");
            }

            // Determine y coordinate
            switch (framePoint)
            {
                case FramePoint.TopLeft:
                case FramePoint.Top:
                case FramePoint.TopRight:
                    y = 0;
                    break;
                case FramePoint.Left:
                case FramePoint.Center:
                case FramePoint.Right:
                    y = this.Height / 2;
                    break;
                case FramePoint.BottomLeft:
                case FramePoint.Bottom:
                case FramePoint.BottomRight:
                    y = this.Height;
                    break;
                default:
                    throw new ArgumentException("Unknown FramePoint.", "framePoint");
            }

            return new Point(x, y);
        }
    }
}
